﻿using System;
using System.Collections.Generic;
using System.Text;
using Architecture.BusinessLayer;
using CommonComponents.Configuration;
using CommonComponents.ExceptionHandling;
using LASLogic.BusinessLayer.DataModel;
using LASLogic.BusinessLayer.Requestor;
using LASLogic.BusinessLayer.Utilities;

namespace LASLogic.BusinessLayer.BusinessLogic
{
    /// <summary>
    /// Clase
    /// </summary>
    public class CompanyManager : BusinessOrchestrator
    {
        #region Public Methods

        /// <summary>
        /// Metodo que permite insertar una nueva compañía a la base de datos
        /// </summary>
        /// <param name="company">Compañia que se quiere insertar en la base de datos</param>
        /// <returns>Indicador si la compañía se insertó correctamente en la base de datos</returns>
        public bool InsertNewCompany(Company company)
        {
            try
            {
                return CompanyRequestor.GetInstance().InsertNewCompany(company.CompanyName, company.TaxIdentification,
                                                                       company.Logo, company.SystemCurrency.CurrencyName, company.LocalCurrency.CurrencyName);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return false;
            }
        }

        /// <summary>
        /// Permite obtener todas las compañias de la base de datos
        /// </summary>
        /// <returns>Lista de compañias de la base de datos</returns>
        public List<Company> GetCompanies()
        {
            try
            {
                return CompanyRequestor.GetInstance().GetCompanies();
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }


        #region Codigo super vulgar... hay que reemplazar todo esto por algo mas decente... para la proxima quizas

        /// <summary>
        /// Método que permite obtener el balance de comprobacion de una empresa
        /// </summary>
        /// <param name="companyId"></param>
        /// <param name="tipoDeMoneda"></param>
        /// <returns>Compañia que se desea obtener el balance</returns>
        public string GetBalanceDeComprobacion(int companyId, string tipoDeMoneda)
        {
            try
            {
                var balanceComprobacion = CompanyRequestor.GetInstance().GetReporte(companyId, Constants.SP_NAME_BALANCE_SHEET);
                if (balanceComprobacion == null || balanceComprobacion.Count == 0) return "No hay suficiente informacion para presentar el balance de comprobacion";
                var stringBuilder = new StringBuilder();
                var info = balanceComprobacion[0];
                stringBuilder.Append(string.Format(Constants.BALANCE_SHEET_FORMAT_HEADER,
                                                   info.NombreEmpresa + " " + info.CedulaJuridica, DateTime.Today));

                var simbolo = balanceComprobacion.Find(e => e.TipoMoneda == tipoDeMoneda).Simbolo;
                var debe = balanceComprobacion.FindAll(e => (e.Codigo.StartsWith("1") || e.Codigo.StartsWith("6") || e.Codigo.StartsWith("8")) && e.TipoMoneda == tipoDeMoneda);
                var haber = balanceComprobacion.FindAll(e => !(e.Codigo.StartsWith("1") || e.Codigo.StartsWith("6") || e.Codigo.StartsWith("8")) && e.TipoMoneda == tipoDeMoneda);

                var totalDebe = new decimal(0.0);
                var totalHaber = new decimal(0.0);
                foreach (var cuenta in debe)
                {

                    var nombre = cuenta.NombreCuenta;
                    stringBuilder.Append(string.Format(Constants.BALANCE_SHEET_FORMAT_INNER,
                                                       simbolo + cuenta.Monto, string.Empty).Remove(0, nombre.Length).Insert(0, nombre));
                    totalDebe = Decimal.Add(totalDebe, cuenta.Monto);
                }
                foreach (var cuenta in haber)
                {
                    var nombre = cuenta.NombreCuenta;
                    stringBuilder.Append(string.Format(Constants.BALANCE_SHEET_FORMAT_INNER,
                                                       "\t",simbolo + cuenta.Monto * -1).Remove(0, nombre.Length).Insert(0, nombre));
                    totalHaber = Decimal.Add(totalHaber, cuenta.Monto);
                }

                stringBuilder.Append(string.Format(Constants.BALANCE_SHEET_FORMAT_BOTTOM, simbolo + totalDebe, simbolo + totalHaber * -1));

                return stringBuilder.ToString();
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        /// <summary>
        /// Método que permite obtener el balance de comprobacion de una empresa
        /// </summary>
        /// <param name="companyId">IdCompañia</param>
        /// <param name="tipoDeMoneda">Selecciona la moneda en que se quiere el resultado</param>
        /// <returns>Compañia que se desea obtener el balance</returns>
        public string GetEstadoResultados(int companyId, string tipoDeMoneda)
        {
            try
            {
                var estadoResultados = CompanyRequestor.GetInstance().GetEstadoResultados(companyId);
                if (estadoResultados == null || estadoResultados.Count == 0) return "No hay suficiente información para presentar el estado de resultados";

                var stringBuilder = new StringBuilder();
                var info = estadoResultados[0];

                stringBuilder.Append(string.Format(Constants.BALANCE_SHEET_FORMAT_HEADER,
                                                   info.NombreEmpresa + " " + info.CedulaJuridica, DateTime.Now));

                var simbolo = estadoResultados.Find(e => e.TipoMoneda == tipoDeMoneda).Simbolo;
                var ingresos = estadoResultados.FindAll(e => e.Tipo == "Ingresos" && e.TipoMoneda == tipoDeMoneda);
                var gastos = estadoResultados.FindAll(e => e.Tipo == "Gastos" && e.TipoMoneda == tipoDeMoneda);
                var ingresosFinan = estadoResultados.FindAll(e => e.Tipo == "Otros Ingresos" && e.TipoMoneda == tipoDeMoneda);
                var egresosFinan = estadoResultados.FindAll(e => e.Tipo == "Otros Gastos" && e.TipoMoneda == tipoDeMoneda);

                var totales = new decimal[4];
                var cuentas = new[] { "", "", "", "" };

                // para cada uno de los ingresos
                foreach (var cuenta in ingresos)
                {

                    var nombre = cuenta.NombreCuenta;
                    cuentas[0] = cuentas[0] + string.Format(Constants.INCOME_STATEMENT_FORMAT_ACCOUNT,
                                                      cuenta.Monto * -1, simbolo).Remove(0, nombre.Length).
                                            Insert(0, nombre);

                    totales[0] = totales[0] + cuenta.Monto;
                }

                // para cada uno de los gastos
                foreach (var cuenta in gastos)
                {

                    var nombre = cuenta.NombreCuenta;
                    cuentas[1] = cuentas[1] + string.Format(Constants.INCOME_STATEMENT_FORMAT_ACCOUNT,
                                                      cuenta.Monto, simbolo).Remove(0, nombre.Length).
                                            Insert(0, nombre);

                    totales[1] = totales[1] + cuenta.Monto;
                }

                // para cada una de los ingresos finacieros
                foreach (var cuenta in ingresosFinan)
                {
                    totales[2] = totales[2] + cuenta.Monto;
                }

                foreach (var cuenta in egresosFinan)
                {
                    totales[3] = totales[3] + cuenta.Monto;
                }

                AppConfigReader appConfig = AppConfigReader.GetInstance();
                var utilidadOperativa = (totales[0] * -1) - totales[1];
                var totalPartidasExtraordinarias = (totales[2] * -1) - totales[3];
                var utilidadAntesImpuesto = utilidadOperativa - totalPartidasExtraordinarias;
                var impuestoRenta = utilidadAntesImpuesto* new decimal(0.1);
                var utilidadNeta = utilidadAntesImpuesto - impuestoRenta;
                var utilidadRetenida = 0; // TODO sacar de la base
                var dividendos = utilidadNeta * new decimal(double.Parse(appConfig.GetProperty(Constants.STOCK_DIVIDENDS_PERCENTAGE)));
                var nuevaUtilidadRetenida = utilidadNeta + utilidadRetenida - dividendos;


                var centro = string.Format(Constants.INCOME_STATEMENT_FORMAT_OUTLINE, cuentas[0], cuentas[1],
                                           utilidadOperativa,
                                           string.Format(Constants.INCOME_STATEMENT_FORMAT_FINANCE, (totales[2] * -1), totales[3], simbolo),
                                           Constants.INCOME_STATEMENT_FORMAT_DIV,
                                           string.Format(Constants.INCOME_STATEMENT_FORMAT_TOTAL_INCOME, (totales[0] * -1), simbolo),
                                           string.Format(Constants.INCOME_STATEMENT_FORMAT_TOTAL_EXPENSES, totales[1], simbolo),
                                           string.Format(Constants.INCOME_STATEMENT_FORMAT_TOTAL_EXTRA, totalPartidasExtraordinarias, simbolo),
                                           simbolo);

                var bottom = string.Format(Constants.INCOME_STATEMENT_FORMAT_BOTTOM, utilidadAntesImpuesto
                                           , (impuestoRenta*-1), utilidadNeta, utilidadRetenida, dividendos,
                                           nuevaUtilidadRetenida, DateTime.Now.Year - 1, DateTime.Now.Year, simbolo);

                var reporte = string.Format("{0}{1}{2}", stringBuilder, centro, bottom);

                return reporte;
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        /// <summary>
        /// Método que permite obtener el estado de resultados de una empresa
        /// </summary>
        /// <param name="companyId"></param>
        /// <param name="tipoDeMoneda"></param>
        /// <returns>Compañia que se desea obtener el estado de resultados</returns>
        public string GetBalanceGeneral(int companyId, string tipoDeMoneda)
        {
            try
            {
                var balanceGeneral = CompanyRequestor.GetInstance().GetBalanceGeneral(companyId);
                if (balanceGeneral == null || balanceGeneral.Count == 0) return "No hay suficiente información para presentar el estado de resultados";

                var stringBuilder = new StringBuilder();
                var info = balanceGeneral[0];

                stringBuilder.Append(string.Format(Constants.INCOME_STATEMENT_FORMAT_HEADER,
                                                   info.NombreEmpresa + " " + info.CedulaJuridica, DateTime.Now));

                var simbolo = balanceGeneral.Find(e => e.TipoMoneda == tipoDeMoneda).Simbolo;
                var activos = balanceGeneral.FindAll(e => e.Tipo.Contains("Activos")  && e.TipoMoneda == tipoDeMoneda);
                var pasivos = balanceGeneral.FindAll(e => e.Tipo.Contains("Pasivos") && e.TipoMoneda == tipoDeMoneda);
                var patrimonio = balanceGeneral.FindAll(e => ( e.Tipo.Contains("Capital") || e.Tipo.Contains("Utilidades") ) && e.TipoMoneda == tipoDeMoneda);

                var totales = new decimal[3];
                var tipoActivos = new[] { "", "", "" };
                // para cada uno de los activos
                foreach (var cuenta in activos)
                {

                    var nombre = cuenta.NombreCuenta;
                    var stringCuenta = "\t" + string.Format(Constants.GENERAL_BALANCE_FORMAT_ACCOUNT,
                                                      cuenta.Monto, simbolo).Remove(0, nombre.Length).
                                            Insert(0, nombre);
                    if (cuenta.Tipo.Contains("Circulantes"))
                    {
                        tipoActivos[0] += stringCuenta;
                    }
                    else if (cuenta.Tipo.Contains("Fijos"))
                    {
                        tipoActivos[1] += stringCuenta;
                    }
                    else if (cuenta.Tipo.Contains("Otros"))
                    {
                        tipoActivos[2] += stringCuenta;
                    }
                    totales[0] += cuenta.Monto;
                }


                var tipoPasivos = new[] { "", "", "" };
                // para cada uno de los pasivos
                foreach (var cuenta in pasivos)
                {

                    var nombre = cuenta.NombreCuenta;
                    var stringCuenta = "\t" + string.Format(Constants.GENERAL_BALANCE_FORMAT_ACCOUNT,
                                                      cuenta.Monto * -1, simbolo).Remove(0, nombre.Length).
                                            Insert(0, nombre);
                    if (cuenta.Tipo.Contains("Circulantes"))
                    {
                        tipoPasivos[0] += stringCuenta;
                    }
                    else if (cuenta.Tipo.Contains("Fijos"))
                    {
                        tipoPasivos[1] += stringCuenta;
                    }
                    else if (cuenta.Tipo.Contains("Otros"))
                    {
                        tipoPasivos[2] += stringCuenta;
                    }
                    totales[1] += cuenta.Monto;
                }

                var tipoPatrimonio = new[] { "", "", "" };
                // para cada uno de los pasivos
                foreach (var cuenta in patrimonio)
                {

                    var nombre = cuenta.NombreCuenta;
                    var stringCuenta = "\t" + string.Format(Constants.GENERAL_BALANCE_FORMAT_ACCOUNT,
                                                      cuenta.Monto * -1, simbolo).Remove(0, nombre.Length).
                                            Insert(0, nombre);
                    if (cuenta.Tipo.Contains("Social"))
                    {
                        tipoPatrimonio[0] += stringCuenta;
                    }
                    else if (cuenta.Tipo.Contains("Retenidas"))
                    {
                        tipoPatrimonio[1] += stringCuenta;
                    }
                    totales[2] += cuenta.Monto;
                }


                var reporte = string.Format(Constants.GENERAL_BALANCE_FORMAT_OUTLINE, tipoActivos[0], tipoActivos[1],
                                            tipoActivos[2], totales[0],
                                            tipoPasivos[0], tipoPasivos[1], tipoPasivos[2], totales[1]*-1,
                                            string.Format("{0}{1}", tipoPatrimonio[0], tipoPatrimonio[1]),
                                            (totales[1] + totales[2])*-1, simbolo);

                return reporte;
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        #endregion

        
        /// <summary>
        /// Permite obtener las monedas de una compañia
        /// </summary>
        /// <param name="companyId">identificador de la compañia</param>
        /// <returns>Las Monedas de una compañia</returns>
        public List<Currency> GetCompanyCurrencies(int companyId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetCompanyCurrencies(companyId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        /// <summary>
        /// Permite obtener todas las compañias de la base de datos
        /// </summary>
        /// <param name="companyId"></param>
        /// <returns>Lista de compañias de la base de datos</returns>
        public Company GetCompany(int companyId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetCompany(companyId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        /// <summary>
        /// Permite añadir telefonos a una compañia
        /// </summary>
        /// <param name="phone">Telefono de la compañia</param>
        /// <param name="companyId">compañía a la que se quiere insertar</param>
        /// <returns>Retorna true si el telefono se insertó, false para lo contrario</returns>
        public bool AddPhonesToCompany(Phone phone, int companyId)
        {
            try
            {
                return CompanyRequestor.GetInstance().AddPhonesToCompany(phone.PhoneType, companyId, phone.Telefono);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return false;
            }
        }

        /// <summary>
        /// Método que permite obtener todos los periodos contables de una compañia
        /// </summary>
        /// <returns>Compañia que se deseaba obtener</returns>
        public List<AccountingMonth> GetAccountingMonths(int periodId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetAccountingMonths(periodId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        /// <summary>
        /// Método que permite obtener todos los periodos contables de una compañia
        /// </summary>
        /// <param name="companyId">Identificador de la compañía</param>
        /// <returns>Periodos contables de la compañía</returns>
        public List<AccountingPeriod> GetAccountingPeriods(int companyId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetAccountingPeriods(companyId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }



        /// <summary>
        /// Permite cambiar el estado de un mes contable a otro
        /// </summary>
        /// <param name="monthId">mes al que se le quiere cambiar el estado</param>
        /// <param name="monthStatus">estado que se convertira en el nuevo mes</param>
        /// <returns>Booleano indicando el estado de la operacion realizada</returns>
        public bool ChangeMonthStatus(int monthId, string monthStatus)
        {
            try
            {
                return CompanyRequestor.GetInstance().ChangeMonthStatus(monthId, monthStatus);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return false;
            }
        }


        /// <summary>
        /// Permite obtener las ciudades para un pais
        /// </summary>
        /// <param name="stateId">Compañia a insertar dentro de la aplicación</param>
        /// <returns>Retorna true si la compañia se insertó, false para lo contrario</returns>
        public List<City> GetCities(int stateId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetCities(stateId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }


        /// <summary>
        /// Permite obtener los estados para un pais
        /// </summary>
        /// <param name="paisId">Compañia a insertar dentro de la aplicación</param>
        /// <returns>Retorna true si la compañia se insertó, false para lo contrario</returns>
        public List<State> GetStates(int paisId)
        {
            try
            {
                return CompanyRequestor.GetInstance().GetStates(paisId);
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }


        /// <summary>
        /// Permite obtener los paises
        /// </summary>
        /// <returns>Lista de los paises</returns>
        public List<Country> GetCountries()
        {
            try
            {
                return CompanyRequestor.GetInstance().GetCountries();
            }
            catch (Exception ex)
            {
                ExceptionHandlingManager.ManageException(ex, ExceptionHandlingManager.ExceptionPolicyType.BusinessLayer);
                return null;
            }
        }

        #endregion

        #region Singleton

        /// <summary>
        /// Única instancia de esta clase
        /// </summary>
        private static CompanyManager _instance;

        /// <summary>
        /// Constructor de la clase CompanyManager 
        /// </summary>
        private CompanyManager()
        {
            Requestor = CompanyRequestor.GetInstance();
        }

        /// <summary>
        /// Método para implementar el patrón de Singleton
        /// </summary>
        /// <returns>Instancia única de la clase</returns>
        public static CompanyManager GetInstance()
        {
            if (_instance == null)
                _instance = new CompanyManager();
            return _instance;
        }

        #endregion
    }
}